/******************************************
 * Author: Dave Spadea, Spadea Enterprises, Inc.
 * Date: November 2010
 * 
 * This code is released by Spadea Enterprises, Inc for general consumption 
 * under the Apache Software License.
 * 
 */


package net.spadea.framework.messaging;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;

import net.spadea.framework.messaging.exceptions.InvalidSensitivityException;

import org.yaml.snakeyaml.*;

public abstract class Message {
	
	public static final String SENSITIVITY_PRIVATE = "private";
	public static final String SENSITIVITY_PUBLIC = "public";

	LinkedHashMap<String,Object> data = new LinkedHashMap<String,Object>();
	String dfltBrokerUrl;
	String dfltControllerQueue;
	
	public Message()
	{
		String messageType = this.getClass().getSimpleName();
		String messageId = UUID.randomUUID().toString();
		
		setHeader("message_id", messageId);
		setHeader("message_type", messageType);
		
		try { setSensitivity(Message.SENSITIVITY_PUBLIC); } catch (InvalidSensitivityException e) {}
		
		setStatus("PENDING_START"); // default initial status
		
		Map<String,String> env = System.getenv();
		dfltBrokerUrl = env.get("FRAMEWORK_BROKER_URL");
		dfltControllerQueue = env.get("FRAMEWORK_CONTROLLER_QUEUE");
	}
	
	private String toYaml() {
		Yaml yaml = new Yaml();
		return yaml.dump(data);
	}
	
	/***
	 *  Set message content sensitivity. This is used by the controller logger to determine whether 
	 * the contents of the message will be logged. The event will always be logged, but the body of
	 * "private" messages will be omitted from the controller_logs table. This will preclude
	 * nice things such as being able to resubmit the event from the portal, but is great for
	 * things like credit card info or client personal info that should not be stored in a 
	 * web-accessible place. 
	 *  
	 *  Valid values: Message.SENSITIVITY_PRIVATE  Message.SENSITIVITY_PUBLIC
	 *  Default: Message.SENSITIVITY_PUBLIC
	 *  
	 *  @return void
	 * @throws InvalidSensitivityException 
	 *  
	 */
	public void setSensitivity(String sensitivity) throws InvalidSensitivityException {
		
		if(sensitivity == null || 
				(    sensitivity != Message.SENSITIVITY_PRIVATE
				  && sensitivity != Message.SENSITIVITY_PUBLIC ) 
		  ) {
			throw new InvalidSensitivityException("Invalid sensitivity! Use Message.SENSITIVITY_PRIVATE or Message.SENSITIVITY_PUBLIC.");
		}
		
		setHeader("sensitivity", sensitivity);
	}
	
	
	public String getSensitivity() {
		return getHeader("sensitivity") == null ? Message.SENSITIVITY_PUBLIC 
												: getHeader("sensitivity");
	}
	
	/***
	 * 
	 * This method should be overridden by application classes to provide an 
	 * appropriate Broker URL in the form: "tcp://hostname:port"
	 * 
	 * Default is value of environment variable: FRAMEWORK_BROKER_URL
	 * @return String
	 */
	public String getBrokerURL() {
		return dfltBrokerUrl;
	}
	
	/***
	 * This method can be overridden to provide a QueueChannel
	 * which should be used for the default send() destination.
	 * Default is value of environment variable: FRAMEWORK_CONTROLLER_QUEUE
	 * 
	 * @return QueueChannel
	 */
	public QueueChannel getDefaultControllerQueue() {
		return new QueueChannel(dfltControllerQueue);
	}
	
	public void setApplicationID(String application_guid) {
		setHeader("application_guid", application_guid);
	}
	
	public String getApplicationID() {
		return getHeader("application_guid");
	}
	
	public void setStatus(String newStatus) {
		setHeader("message_status", newStatus);
	}
	
	public String getStatus() {
		return getHeader("message_status");
	}
	
	public void load (String yamlData) {
		Yaml yaml = new Yaml();
		data = (LinkedHashMap<String, Object>) yaml.load(yamlData);
	}
	
	public String getMessageType() {
		return (String)get("Header", "message_type");
	}
	
	public void send() {
		send(getDefaultControllerQueue());
	}
	
	public void send(Channel c) {
		
		String msg_yaml = toYaml();
		
		BrokerPublisher pub = new BrokerPublisher(getBrokerURL());
		
		if (c.getType().equals("TOPIC"))
			pub.send(new TopicChannel(c), msg_yaml);
		else if (c.getType().equals("QUEUE"))
			pub.send(new QueueChannel(c), msg_yaml);
		
	}
	
	public void setHeader(String header_name, String value)
	{
		set("Header", header_name, value);
	}
	
	public String getHeader(String header_name)
	{
		return (String)get("Header", header_name);
	}
	
	public LinkedHashMap<String,Object> getSection(String section_name)
	{
		if (!data.containsKey(section_name)) {
			createSection(section_name);
		}
		
		return (LinkedHashMap<String, Object>) data.get(section_name);
		
	}
	
	public void createSection(String section_name)
	{
		if (! data.containsKey(section_name)) {
			data.put(section_name, new LinkedHashMap<String, Object>());	
		} 
	}
	
	public void set(String sectionName, String keyName, Object value)
	{
		LinkedHashMap<String, Object> section;
		section = getSection(sectionName);
		
		if (section.containsKey(keyName)) {
			section.remove(keyName);
		}

		section.put(keyName, value);
	}
	
	public Object get(String section_name, String key_name)
	{
		LinkedHashMap<String, Object> section;
		section = getSection(section_name);
		
		return section.get(key_name);
	}
	
	public void set(String key_name, Object value)
	{
		set(getMessageType(), key_name, value);
	}
	
	public Object get(String key_name)
	{
		return get(getMessageType(), key_name);
	}
	
	public String toString()
	{
		return toYaml();
	}
	
	public Object convertTo(Class<?> destRequestType) throws InstantiationException, IllegalAccessException {
		Message newReq = (Message)destRequestType.newInstance();
		newReq.load(toYaml());
		newReq.setStatus("PENDING_START");
		newReq.setHeader("message_type", destRequestType.getSimpleName());
		
		if (newReq.getHeader("message_subtype") != null) {
			newReq.setHeader("message_subtype", "");
		}
		
		return newReq;
	}
	
}
